package com.kinghunt.misp.service.impl.userx;

import com.kinghunt.misp.common.constant.PermissionConstant;
import com.kinghunt.misp.common.dto.common.Tree;
import com.kinghunt.misp.common.dto.userx.PermissionDTO;
import com.kinghunt.misp.common.utils.PermissionConverter;
import com.kinghunt.misp.dal.dao.userx.*;
import com.kinghunt.misp.dal.model.userx.*;
import com.kinghunt.misp.service.api.userx.PermissionReadService;
import com.bygoodtech.kings.base.utils.ConvertUtil;
import com.bygoodtech.kings.common.dto.ApiResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 权限
 * @Author jiangtao
 * @Date 2023/7/5 22:05
 */
@Service
public class PermissionReadServiceImpl implements PermissionReadService {

    @Autowired(required = false)
    PermissionDAO permissionDAO;
    @Autowired(required = false)
    RolePermissionDAO rolePermissionDAO;
    @Autowired(required = false)
    UserPermissionDAO userPermissionDAO;
    @Autowired(required = false)
    UserPermissionBlacklistDAO userPermissionBlacklistDAO;
    @Autowired(required = false)
    RoleDAO roleDAO;
    @Autowired(required = false)
    UserRoleDAO userRoleDAO;
    @Autowired(required = false)
    UserDAO userDAO;
    
    @Override
    public ApiResult<Tree<PermissionDTO>> getSystemPermissionTree(String appCode) {
        Tree<PermissionDTO> permissionTree = buildPermissionTree(appCode, null, null);
        return ApiResult.ok(permissionTree);
    }

    @Override
    public ApiResult<Tree<PermissionDTO>> getRolePermissionTree(Long roleId) {
        RoleDO roleDO = roleDAO.selectByPrimaryKey(roleId);
        if (Objects.isNull(roleDO)) {
            return ApiResult.failOfMessage("role not exist");
        }

        List<RolePermissionDO> rolePermissionDOs = rolePermissionDAO.selectByRoleIds(Arrays.asList(roleId));
        Set<Long> selectedPermissions = Optional.ofNullable(rolePermissionDOs).orElse(new ArrayList<>()).stream()
                .map(RolePermissionDO::getPermissionId).collect(Collectors.toSet());

        Tree<PermissionDTO> permissionTree = buildPermissionTree(roleDO.getAppCode(), selectedPermissions, null);
        return ApiResult.ok(permissionTree);
    }

    @Override
    public ApiResult<Tree<PermissionDTO>> getUserPermissionTree(Long userId) {
        List<UserDO> userByIds = userDAO.getUserByIds(Collections.singletonList(userId));
        if (CollectionUtils.isEmpty(userByIds)) {
            return ApiResult.failOfMessage("user not exist");
        }

        Set<Long> selectedPermissions = new HashSet<>();
        Set<Long> blacklistPermissions = new HashSet<>();

        List<Long> userRoleIds = userRoleDAO.selectRoleIdsByUserId(userId);
        if (!CollectionUtils.isEmpty(userRoleIds)) {
            List<RolePermissionDO> rolePermissionDOS = rolePermissionDAO.selectByRoleIds(userRoleIds);
            selectedPermissions.addAll(rolePermissionDOS.stream().map(RolePermissionDO::getPermissionId).collect(Collectors.toSet()));
        }
        List<UserPermissionDO> userPermissionDOS = userPermissionDAO.selectByUserId(userId);
        if (!CollectionUtils.isEmpty(userPermissionDOS)) {
            selectedPermissions.addAll(userPermissionDOS.stream().map(UserPermissionDO::getPermissionId).collect(Collectors.toSet()));
        }

        List<UserPermissionBlacklistDO> blacklistDOS = userPermissionBlacklistDAO.selectByUserId(userId);
        if (!CollectionUtils.isEmpty(blacklistDOS)) {
            blacklistPermissions.addAll(blacklistDOS.stream().map(UserPermissionBlacklistDO::getPermissionId).collect(Collectors.toSet()));
        }

        Tree<PermissionDTO> permissionTree = buildPermissionTree(userByIds.get(0).getAppCode(), selectedPermissions, blacklistPermissions);
        return ApiResult.ok(permissionTree);
    }

    @Override
    public ApiResult<List<PermissionDTO>> getRolePermissions(Long roleId) {
        return getRolePermissions(Arrays.asList(roleId));
    }

    @Override
    public ApiResult<List<PermissionDTO>> getRolePermissions(List<Long> roleIds) {
        List<RolePermissionDO> rolePermissionDOS = rolePermissionDAO.selectByRoleIds(roleIds);
        List<PermissionDTO> PermissionDTOs = PermissionConverter.convertFromRolePermissionDOs(rolePermissionDOS);
        return ApiResult.ok(PermissionDTOs);
    }

    @Override
    public ApiResult<List<PermissionDTO>> getUserPermissions(Long userId) {
        Set<Long> selectedPermissions = new HashSet<>();
        Set<Long> blacklistPermissions = new HashSet<>();

        List<Long> userRoleIds = userRoleDAO.selectRoleIdsByUserId(userId);
        if (!CollectionUtils.isEmpty(userRoleIds)) {
            List<RolePermissionDO> rolePermissionDOS = rolePermissionDAO.selectByRoleIds(userRoleIds);
            selectedPermissions.addAll(rolePermissionDOS.stream().map(RolePermissionDO::getId).collect(Collectors.toSet()));
        }
        List<UserPermissionDO> userPermissionDOS = userPermissionDAO.selectByUserId(userId);
        if (!CollectionUtils.isEmpty(userPermissionDOS)) {
            selectedPermissions.addAll(userPermissionDOS.stream().map(UserPermissionDO::getPermissionId).collect(Collectors.toSet()));
        }

        List<UserPermissionBlacklistDO> blacklistDOS = userPermissionBlacklistDAO.selectByUserId(userId);
        if (!CollectionUtils.isEmpty(blacklistDOS)) {
            blacklistPermissions.addAll(blacklistDOS.stream().map(UserPermissionBlacklistDO::getPermissionId).collect(Collectors.toSet()));
        }

        selectedPermissions.removeAll(blacklistPermissions);
        List<PermissionDO> permissionDOS = permissionDAO.selectByIds(new ArrayList<>(selectedPermissions));
        List<PermissionDTO> PermissionDTOs = ConvertUtil.convert(permissionDOS, PermissionDTO.class);

        return ApiResult.ok(PermissionDTOs);
    }

    /**
     * 构建权限树
     *
     * @param appCode              应用code
     * @param selectedPermissions  白名单
     * @param blacklistPermissions 黑名单
     * @return 权限树
     */
    private Tree<PermissionDTO> buildPermissionTree(final String appCode, final Set<Long> selectedPermissions, final Set<Long> blacklistPermissions) {
        Tree<PermissionDTO> permissionTree;
        Map<Long, List<PermissionDTO>> permissionMap = getAllPermissionParentIdMap(appCode);
        if (CollectionUtils.isEmpty(permissionMap)) {
            return null;
        }
        PermissionDTO currentNode = permissionMap.get(PermissionConstant.PERMISSION_ANCESTOR_PARENT_ID).get(0);
        permissionTree = processTree(currentNode, permissionMap, selectedPermissions, blacklistPermissions);
        return permissionTree;
    }

    /**
     * 递归构造权限树
     */
    private Tree<PermissionDTO> processTree(PermissionDTO currentNode,
                                            final Map<Long, List<PermissionDTO>> permissionMap,
                                            final Set<Long> selectedPermissions,
                                            final Set<Long> blacklistPermissions) {
        if (Objects.isNull(currentNode)) {
            return null;
        }

        // 处理白名单
        Tree<PermissionDTO> permissionTree = new Tree<>();
        if (CollectionUtils.isEmpty(selectedPermissions)) {
            currentNode.setSelected(false);
        } else {
            currentNode.setSelected(selectedPermissions.contains(currentNode.getId()));
        }

        // 处理黑名单
        if (!CollectionUtils.isEmpty(blacklistPermissions) && blacklistPermissions.contains(currentNode.getId())) {
            currentNode.setSelected(false);
        }

        permissionTree.setCurrentNode(currentNode);
        List<PermissionDTO> children = Optional.ofNullable(permissionMap.get(currentNode.getId())).orElse(Collections.EMPTY_LIST);
        List<Tree<PermissionDTO>> childrenTree = new ArrayList<>();
        for (PermissionDTO c : children) {
            childrenTree.add(processTree(c, permissionMap, selectedPermissions, blacklistPermissions));
        }
        permissionTree.setChildren(childrenTree);

        return permissionTree;
    }

    /**
     * 获取应用下所有权限的 {parentId - 权限} map
     *
     * @param appCode 应用code
     * @return map
     */
    private Map<Long, List<PermissionDTO>> getAllPermissionParentIdMap(String appCode) {
        List<PermissionDO> permissionDOS = permissionDAO.selectByAppCode(appCode);
        if (CollectionUtils.isEmpty(permissionDOS)) {
            return Collections.EMPTY_MAP;
        }
        List<PermissionDTO> PermissionDTOs = ConvertUtil.convert(permissionDOS, PermissionDTO.class);
        Map<Long, List<PermissionDTO>> permissionMap = PermissionDTOs.stream()
                .collect(Collectors.groupingBy(PermissionDTO::getParentId));
        return permissionMap;
    }
}
